package joymouse;

import java.util.HashMap;
import joymouse.logger.Error;
import joymouse.logger.ExceptionError;
import joymouse.logger.Logger;
import joymouse.logger.Warning;
import net.java.games.input.*;

/**
 * The main class of the application.
 *
 * @author Ben
 */
public class JoyMouse {

    private HashMap<String, String> arguments = new HashMap<>();
    private String[] argNames = {
        "controller",
        "relative",
        "version",
        "help"
    };
    private static final String HELP_MSG = "A simple utility for controlling "
            + "a mouse with a joystick or other controller.\n"
            + "\n"
            + "Options:\n"
            + "-c, --controller CONTROLLER: The controller to use.\n"
            + "-r, --relative: Use relative mouse control mode(recommended); if not specified, absolute control mouse is used.\n"
            + "-v, --version: Print current version.\n"
            + "-h, --help: Show this help.";
    private static final String VERSION = "JoyMouseJava 1.1.0";
    private static final String VERSION_INFO = VERSION + "\n"
            + "Copyright (C) 2013 Ben Wolsieffer\n"
            + "MIT License <http://opensource.org/licenses/MIT>\n"
            + "This is open source software: you are free to change and redistribute it.\n"
            + "There is NO WARRANTY, to the extent permitted by law.";

    /**
     * Gets a joystick with the specified name. If one is not found, it returns
     * {@code null}.
     *
     * @param contArr The array of {@link Controller}s to search.
     * @param name The name of the joystick.
     * @return The joystick with the specified name.
     */
    private Controller getJoystickByName(Controller[] contArr, String name) {
        Controller cont = null;
        for (int i = 0; i < contArr.length; i++) {
            if (contArr[i].getName().equalsIgnoreCase(name) && contArr[i].getType() == Controller.Type.STICK) {
                cont = contArr[i];
            }
        }
        return cont;
    }

    /**
     * Gets the first joystick contained in {@code contArr}. If a joystick is
     * not found, it returns {@code null}.
     *
     * @param contArr The array of {@link Controller}s to search.
     * @return The first joystick in {@code contArr}.
     */
    private Controller getFirstStick(Controller[] contArr) {
        Controller cont = null;
        for (int i = 0; i < contArr.length; i++) {
            if (contArr[i].getType() == Controller.Type.STICK) {
                cont = contArr[i];
            }
        }
        return cont;
    }

    /**
     * Processes the command line arguments passes in {@code args} and adds the
     * key value pairs to {@code arguments}. The possible argument names are
     * stored in {@code argNames}.
     *
     * @param args the array of arguments
     */
    private void processArgs(String args[]) {
        for (int n = 0; n < argNames.length; n++) {
            String name = argNames[n];
            String longName = "--" + name;
            String shortName = longName.substring(1, 3);
            for (int a = 0; a < args.length; a++) {
                String arg = args[a];
                if (arg.equals(longName) || arg.equals(shortName)) {
                    if (!arguments.containsKey(name)) {
                        String value = null;
                        if (a + 1 < args.length) {
                            value = args[a + 1];
                            if (value.startsWith("-")) {
                                value = null;
                            } else {
                                a++;
                            }
                        }
                        arguments.put(name, value);
                    } else {
                        Logger.getLogger().log(Warning.ARGUMENT_INCLUDED_TWICE);
                    }
                }
            }
        }
    }

    /**
     * Main entry point for the application. Catches all exceptions (in this
     * thread) and passes them to the logger.
     *
     * @param args
     */
    public static void main(String args[]) {
        try {
            new JoyMouse(args);
        } catch (Exception ex) {
            Logger.getLogger().log(new ExceptionError(ex));
        }
    }

    /**
     * Creates a new main application instance.
     *
     * @param args
     */
    public JoyMouse(String[] args) {
        processArgs(args);
        Controller[] controllers = ControllerEnvironment.getDefaultEnvironment().getControllers();
        Controller joystick;
        boolean showVersion = arguments.containsKey("version");
        boolean showHelp = arguments.containsKey("help");
        if (showHelp) {
            System.out.println(VERSION);
            System.out.println(HELP_MSG);
        } else if (showVersion) {
            System.out.println(VERSION_INFO);

        } else {
            boolean relative = arguments.containsKey("relative");
            String controllerName = arguments.get("controller");
            if (controllerName != null) {
                joystick = getJoystickByName(controllers, controllerName);
                if (joystick == null) {
                    Logger.getLogger().log(Error.INVALID_JOYSTICK_NAME);
                }
            } else {
                joystick = getFirstStick(controllers);
                if (joystick == null) {
                    Logger.getLogger().log(Error.NO_JOYSTICK_FOUND);
                }
            }
            Logger.getLogger().log("Using joystick: " + joystick.getName());
            
            MouseController mouseController;
            KeyController keyController = new KeyController(joystick);
            
            if (relative) {
                mouseController = new RelativeMouseController(joystick);
//                ControllerManager.addDeviceController(new RelativeMouseController(joystick));
            } else {
                mouseController = new AbsoluteMouseController(joystick);
//                ControllerManager.addDeviceController(new AbsoluteMouseController(joystick));
            }
            
            // Memory hogging code
//            ControllerManager.addDeviceController(new KeyController(joystick));
//            ControllerManager.run();
            
            for(;;){
                joystick.poll();
                mouseController.run();
                keyController.run();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                }
            }
        }
    }
}
